/*
 * 文件名称: RequestUtils.java Copyright 2011-2015 Ximalaya All right reserved.
 */
package top.zxybird.common.util;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import top.zxybird.common.model.ClientInfo;

/**
 * http request辅助类
 * 
 * @author cossea
 */
public final class RequestUtils {
  private static final Logger LOG = LoggerFactory.getLogger(RequestUtils.class);

  private RequestUtils() {
  }

  public static final String DEFAULT_CHARARSET = "UTF-8";

  public static final String POSITION_KEY = "NSUP"; // cookie中记录地理位置信息的Key
  public static final String SYMBOL_DOT = ",";
  public static final String X_REAL_IP = "X-Real-IP";
  public static final String X_FORWORDED_FOR = "X-Forwarded-For";
  public static String USER_AGENT = "user-agent";
  public static String CLIENT_VERSION = "U_CLIENT_VERSION";
  public static String CLIENT_DEVICE = "U_CLIENT_DEVICE";
  public static String CLIENT_DEVICE_ID = "U_CLIENT_DEVICE_ID";
  public static String CLIENT_BUNDLE_ID = "U_CLIENT_BUNDLE_ID";
  public static String CLIENT_CHANNEL_ID = "U_CLIENT_CHANNEL_ID";
  public static String CLIENT_POSITION_X = "U_CLIENT_POSITION_X";
  public static String CLIENT_POSITION_Y = "U_CLIENT_POSITION_Y";
  public static String CLIENT_MAC = "U_CLIENT_MAC";
  public static final String USER_ID = "currentUserId";
  public static final String USER_SESSION_ID = "XIMALAYA_LOGIN_USER_SESSION_ID";
  public static final String REMEMBER_ME = "remember_me";

  public static String getUserAgent(HttpServletRequest request) {
    if (null == request) {
      return null;
    }
    return request.getHeader(USER_AGENT);
  }

  /**
   * 获取http请求中包含的网安数据及地理位置信息
   * 
   * @param request 请求对象
   * @return 结果map包含key:
   *         <p>
   *         {"ip","mac","deviceId","positionX","positionY","version","device"}
   */
  public static Map<String, String> getNetSecurityData(HttpServletRequest request) {
    String ip = getClientIp(request);
    String mac = getCurrentMac(request);
    String deviceId = getCurrentDeviceId(request);
    String positionX = getCurrentPostionx(request);
    String positionY = getCurrentPostiony(request);
    String version = getCurrentVersion(request);
    String device = getCurrentDevice(request);
    Map<String, String> data = new HashMap<String, String>(8);
    if (ip != null) {
      data.put("ip", ip);
    }
    if (mac != null) {
      data.put("mac", mac);
    }
    if (deviceId != null) {
      data.put("deviceId", deviceId);
    }
    if (positionX != null) {
      data.put("positionX", positionX);
    }
    if (positionY != null) {
      data.put("positionY", positionY);
    }
    if (version != null) {
      data.put("version", version);
    }
    if (device != null) {
      data.put("device", device);
    }
    return data;
  }

  /**
   * 获取中间件消息对象中网安数据
   * 
   * @param message 消息对象
   * @return 结果map包含key:
   *         <p>
   *         {"ip","mac","deviceId","positionX","positionY","version","device"}
   */
  public static Map<String, String> getNetSecurityData(Map<String, Object> message) {
    Map<String, String> data = new HashMap<String, String>(8);
    if (!CollectionUtils.isEmpty(message)) {
      String ip = ParamUtils.getString(message.get("ip"));
      String mac = ParamUtils.getString(message.get("mac"));
      String deviceId = ParamUtils.getString(message.get("deviceId"));
      String positionX = ParamUtils.getString(message.get("positionX"));
      String positionY = ParamUtils.getString(message.get("positionY"));
      String version = ParamUtils.getString(message.get("version"));
      String device = ParamUtils.getString(message.get("device"));
      if (ip != null) {
        data.put("ip", ip);
      }
      if (mac != null) {
        data.put("mac", mac);
      }
      if (deviceId != null) {
        data.put("deviceId", deviceId);
      }
      if (positionX != null) {
        data.put("positionX", positionX);
      }
      if (positionY != null) {
        data.put("positionY", positionY);
      }
      if (version != null) {
        data.put("version", version);
      }
      if (device != null) {
        data.put("device", device);
      }
    }
    return data;
  }

  /**
   * 判断是否是有效的ip
   * <p>
   * 不合法的ip包括非IP字符串以及以下IP段
   * <li>10.0.0.0-10.255.255.255</li>
   * <li>127.0.0.0-127.255.255.255</li>
   * <li>169.254.0.0-169.254.255.255</li>
   * <li>172.16.0.0-172.31.255.255</li>
   * <li>192.168.0.0-192.168.255.255</li>
   * 
   * @param ip ip地址
   * @return 有效是true，否则false
   */
  public static boolean isValidIp(String ip) {
    boolean valid = true;
    try {
      if (StringUtils.isEmpty(ip)) {
        valid = false;
      }
      ip = ip.replaceAll(" ", "");
      String[] stringIpSegments = ip.split("\\.");
      if (stringIpSegments.length == 4) {
        int first = Integer.parseInt(stringIpSegments[0]);
        int second = 0;
        switch (first) {
          case 10:
            valid = false;
            break;
          case 127:
            valid = false;
            break;
          case 169:
            second = Integer.parseInt(stringIpSegments[1]);
            if (second == 254) {
              valid = false;
            }
            break;
          case 172:
            second = Integer.parseInt(stringIpSegments[1]);
            if (16 <= second && second < 32) {
              valid = false;
            }
            break;
          case 192:
            second = Integer.parseInt(stringIpSegments[1]);
            if (second == 168) {
              valid = false;
            }
            break;
          default:
            break;
        }
      } else {
        valid = false;
      }
    } catch (Exception e) {
      valid = false;
    }
    return valid;
  }

  /**
   * 从HttpRequest中获取请求的ip
   * 
   * @param request HttpServletRequest instance
   * @return ip
   */
  public static String getClientIp(HttpServletRequest request) {
    String realIp = null;
    String forwordedIp = request.getHeader(X_FORWORDED_FOR);
    if (StringUtils.hasText(forwordedIp) && !"unKnown".equalsIgnoreCase(forwordedIp)) {
      // 多次反向代理后会有多个ip值，第一个公网ip才是真实ip
      String ipStr[] = forwordedIp.split(",");
      for (String anIpStr : ipStr) {
        if (isValidIp(anIpStr)) {
          realIp = anIpStr;
          break;
        }
      }
    }
    if (realIp == null) {
      realIp = request.getHeader(X_REAL_IP);
      if (StringUtils.isEmpty(realIp) || "unKnown".equalsIgnoreCase(realIp)) {
        realIp = request.getRemoteAddr();
      }
    }
    return realIp;
  }

  public static String getCurrentChannelId(ServletRequest request) {
    return (String) request.getAttribute(CLIENT_CHANNEL_ID);
  }

  public static String getCurrentDevice(ServletRequest request) {
    return (String) request.getAttribute(CLIENT_DEVICE);
  }

  public static String getCurrentDeviceId(ServletRequest request) {
    return (String) request.getAttribute(CLIENT_DEVICE_ID);
  }

  public static String getCurrentMac(ServletRequest request) {
    return (String) request.getAttribute(CLIENT_MAC);
  }

  public static String getCurrentPostionx(ServletRequest request) {
    return (String) request.getAttribute(CLIENT_POSITION_X);
  }

  public static String getCurrentPostiony(ServletRequest request) {
    return (String) request.getAttribute(CLIENT_POSITION_Y);
  }

  public static String getCurrentBundleId(ServletRequest request) {
    return (String) request.getAttribute(CLIENT_BUNDLE_ID);
  }

  public static String getCurrentVersion(ServletRequest request) {
    return (String) request.getAttribute(CLIENT_VERSION);
  }

  public static String buildLogMessage(Exception ex, HttpServletRequest request) {
    StringBuilder sb = new StringBuilder();
    // request uri path
    sb.append("unexpected exception with request url ").append(request.getRequestURI()).append(" and method = ")
        .append(request.getMethod()).append("\r\n");
    @SuppressWarnings("unchecked")
    Map<String, String[]> paramMap = request.getParameterMap();
    StringBuilder paramSb = new StringBuilder();
    for (Map.Entry<String, String[]> entry : paramMap.entrySet()) {
      try {
        paramSb.append(entry.getKey()).append(":").append(Arrays.toString(entry.getValue())).append(", ");
      } catch (Throwable e) {
        LOG.error("operate param {} error!", entry, e);
      }
    }
    // request parameter
    sb.append("Parameter are ").append(paramSb.toString()).append("\r\n");
    // client info
    ClientInfo clientInfo = ClientUtils.assembleClientInfo(request);
    sb.append("client info is ").append(clientInfo).append("\r\n");
    // request cookie
    Cookie[] cookies = request.getCookies();
    if (cookies != null) {
      sb.append("Cookies are [");
      for (int i = 0; i < cookies.length; i++) {
        if (i > 0) {
          sb.append(", ");
        }
        sb.append(cookies[i].getName()).append("=").append(cookies[i].getValue());
      }
      sb.append("]\r\n");
    }
    return sb.toString();
  }
}
